package compilers.table;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import compilers.node.Identifier;

public class MemScope {

	private MemScope fParentScope = null;
	Map<String, Integer> fVariables = new HashMap<String, Integer>();
	Map <String, Integer> fFunctions = new HashMap<String, Integer>();
	Map<String, Identifier> fArrays = new HashMap<String, Identifier>();
	int fNextAddress;
	int fNextParamAddress = 5;
	String fName;
	int fLabelCount = 0;
	boolean fFunctionScope = false;
	boolean loop = false;
	
	public void setLoop(boolean x) {
		loop = x;
	}
	
	public MemScope(MemScope parentScope, String name) {
		setfParentScope(parentScope);
		fName = "func_" + name;
		fFunctionScope = true;
	}
	
	public String getLastLoopLabel() {
		if(!loop) {
			return fParentScope.getLastLoopLabel();
		}
		return fName + "_" + fLabelCount;
	}
	
	public String getUniqueLabel() {
		fLabelCount++;
		return fName + "_" + fLabelCount;
	}
	
	public void setStart(int ssp) {
		fNextAddress = ssp;
	}
	
	public MemScope(MemScope parentScope) {
		if (parentScope != null) {
			setfParentScope(parentScope);
			fNextAddress = parentScope.getNextAddress();
		}
		fName = UUID.randomUUID().toString().replace("-", "_");
	}
	
	public Identifier getArray(String name) {
		Identifier tmp = new Identifier(name);
		if (!tmp.getIndices(null).isEmpty()) {
			return tmp;
		}
		return fArrays.get(name);
	}
	
	public void addArray(String name) {		
		String orig = name;
		char c[] = name.toCharArray();
		for (int i = 0; i < c.length; i++) {
			if (c[i] == '[') {
				name = name.substring(0, i);
				break;
			}
		}
		
		fArrays.put(name, new Identifier(orig));
	}
		
	public String getName() {
		if (getfParentScope() != null) {
			if (getfParentScope().getfParentScope() != null)
				return fName + "_" + getfParentScope().getName();
		}
		return fName;
	}
	
	public void setNextAddress(int next) {
		fNextAddress = next;
	}
	
	public int getNextAddress() {
		return fNextAddress;
	}
	
	public void addFunction(String name) {
		fFunctions.put(name, getDepth(0));
	}
	
	public int getFunctionDepth(String name) {
		if (fFunctions.containsKey(name)) {
			return fFunctions.get(name);
		} else {
			return getfParentScope().getFunctionDepth(name);
		}
	}
	
	public int getDepth(int i) {
		if (getfParentScope() == null) {
			return i;
		} else {
			if (this.fFunctionScope) {
				return getfParentScope().getDepth(i+1);	
			} else {
				return getfParentScope().getDepth(i);	
			}
		}
	}
	
	public int getDepth(String name, int i) {		
		//Nasty bugfix
		int ind = name.indexOf('[');
		if(ind> 0) {
			int count = name.split("\\Q[\\E", -1).length - 1;
			String name1 = name.substring(0,ind);
			while(count > 0) {
				name1 = name1 + "[0]";
				count--;
			}
			if(fVariables.containsKey(name1)) {
				return i;
			}
			
		}
		
		if (fVariables.containsKey(name)) {
			return i;
		} else {
			if (getfParentScope() == null) {
				Identifier tmp = new Identifier(name);
				if (tmp.getIndices(null).isEmpty()) {
					System.out.println("Error in get MemScope, could not find " + name);
				} else {
					//System.out.println("Error in get MemScope, could not find " + name);
					return i;
				}
			}
			if (this.fFunctionScope) {
				return getfParentScope().getDepth(name, i+1);	
			} else {
				return getfParentScope().getDepth(name, i);	
			}
		}
	}
	
	public boolean hasFunction(String name) {
		if (fFunctions.containsKey(name)) {
			return true;
		} else {
			if (getfParentScope() == null) {
				return false;
			}
			if (getfParentScope().getfParentScope() == null) {
				return false;
			}
			return getfParentScope().hasFunction(name);
		}
	}
	
	public void add(String name) {
		if (this.get(name) == -1) {
			fVariables.put(name, fNextAddress);
			fNextAddress++;
		}
	}	
	
	public void addParam(String name) {
		fVariables.put(name, fNextParamAddress);
		fNextParamAddress++;
	}
	
	public boolean isArray(String name) {
		Identifier tmp = new Identifier(name);
		if (!tmp.getIndices(null).isEmpty()) {
			return true;
		}
		
		for (Map.Entry<String, Integer> entry : fVariables.entrySet()) {
			String key = new String(entry.getKey());
			int ind = key.indexOf("[");
			if(ind > 0) {
				key = key.substring(0,ind);
				if(key.equals(name)) {
					return true;
				}
			}
		}
		return false;
	}
	
	public int get(String name) {
		if (fVariables.containsKey(name)) {
			return fVariables.get(name);
		} else {
			if (getfParentScope() == null) {
				Identifier tmp = new Identifier(name);
				if (tmp.getIndices(null).isEmpty()) {
					//System.out.println("Error in get MemScope, could not find " + name);
					return -1;
				} else {
					//System.out.println("Error in get MemScope, could not find " + name);
					return -1;
				}
			}
			return getfParentScope().get(name);				
		}
	}
	
	public boolean has(String name) {
		if (fVariables.containsKey(name)) {
			return true;
		} else {
			if (getfParentScope() == null) {
				return false;
			}
			return getfParentScope().has(name);
		}
	}

	public MemScope getfParentScope() {
		return fParentScope;
	}

	public void setfParentScope(MemScope fParentScope) {
		this.fParentScope = fParentScope;
	}
}